use actix_http::http::{header, StatusCode};
use actix_http::Response;
use actix_web::{Error as WebError, ResponseError};
use awc::error::HttpError as AWCHttpError;
use serde::Serialize;
use serde_json::{to_string, Error as JsonError};
use sqlx::Error as SQLError;
use std::fmt::Write;
use std::io::Error as IOError;
use thiserror::Error;
pub type AppResult<T> = Result<T,AppError>;
#[derive(Debug, Error)]
pub enum AppError {
    #[error("io error:{0}")]
    IO(#[from] IOError),
    #[error("web error:{0}")]
    WEB(#[from] WebError),
    #[error("json error:{0}")]
    JSON(#[from] JsonError),
    #[error("awc http error:{0}")]
    AWCHttp(#[from] AWCHttpError),
    #[error("sql error:{0}")]
    SQL(#[from] SQLError),
    #[error("not found:{0}")]
    NotFound(String),
    #[error("forbidden:{0}")]
    Forbidden(String),
    #[error("unauthorized:{0}")]
    Unauthorized(String),
    #[error("bad request:{0}")]
    BadRequest(String),
}
#[derive(Debug, Serialize)]
pub struct ErrorJSONBody {
    msg: String,
}
macro_rules! impl_into_status_code {
    ($type:ty) => {
        impl Into<StatusCode> for $type {
            fn into(self) -> StatusCode {
                match self {
                    AppError::BadRequest(_) => StatusCode::BAD_REQUEST,
                    AppError::NotFound(_) => StatusCode::NOT_FOUND,
                    AppError::Forbidden(_) => StatusCode::FORBIDDEN,
                    AppError::Unauthorized(_) => StatusCode::UNAUTHORIZED,
                    AppError::SQL(ref sql) => {
                        match sql {
                            SQLError::RowNotFound => {
                                StatusCode::NOT_FOUND
                            },
                            _=> StatusCode::INTERNAL_SERVER_ERROR,
                        }
                    },
                    _ => StatusCode::INTERNAL_SERVER_ERROR,
                }
            }
        }
    };
}
impl_into_status_code!(AppError);
impl_into_status_code!(&AppError);


impl ResponseError for AppError {
    fn status_code(&self) -> StatusCode {
        self.into()
    }
    fn error_response(&self) -> Response {
        let mut resp = Response::new(self.status_code());
        let mut buf = String::new();
        let _ = write!(&mut buf, "{}", self);
        resp.headers_mut().insert(
            header::CONTENT_TYPE,
            header::HeaderValue::from_static("application/json; charset=utf-8"),
        );
        resp.set_body(actix_http::body::Body::from(
            to_string(&ErrorJSONBody { msg: buf }).unwrap_or("serialize error".into()),
        ))
    }
}
